package ar.edu.itba.it.cg.grupo01.impl.shape;

import java.util.Set;

import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import ar.edu.itba.it.cg.grupo01.api.Intersection;
import ar.edu.itba.it.cg.grupo01.api.Ray;
import ar.edu.itba.it.cg.grupo01.api.shape.Shape;
import ar.edu.itba.it.cg.grupo01.impl.IntersectionFactory;

public class Triangle extends AbstractShape {

	private Point3d first, second, third;

	public Triangle(final Point3d first, final Point3d second,
			final Point3d third, IntersectionFactory intersectionFactory) {
		super(intersectionFactory);
		this.first = first;
		this.second = second;
		this.third = third;
	}

	@Override
	public Intersection<? extends Shape> intersects(final Ray ray) {

		Plane plane = getContainingPlane();
		Set<Point3d> intersections = plane.intersects(ray).getIntersections();

		if (intersections.isEmpty())
			return intersectionFactory.emptyIntersection();

		// point is in triangle's plane, now we check its inside triangle

		// there is only one point...
		for (Point3d testPoint : intersections) {

			if(this.contains(testPoint))
				return intersectionFactory.createIntersection(intersections);
		}
		return intersectionFactory.emptyIntersection();
		

		
	}

	/**
	 * Returns the containing plane for the triangle should be private
	 * 
	 * @return
	 */
	// TODO: PASARLA A PRIVATE

	public Plane getContainingPlane() {
		// devuelve [a b c d] ax+by+cz+d=0
		Vector3d dir1 = new Vector3d(first);
		Vector3d dir2 = new Vector3d(first);
		dir1.sub(second);
		dir2.sub(third);

		// TODO Crearle un constructor a plane que reciba 3 puntos
		// calculo la normal tomando dos vectores del plano
		Vector3d planeNormal = new Vector3d();
		planeNormal.cross(dir1, dir2);

		return new Plane(third, planeNormal, this.intersectionFactory);
	}

	public boolean contains(Point3d testPoint) {
		if (testPoint.equals(third) || testPoint.equals(first)
				|| testPoint.equals(second)) {
			return true;
		}
		/*
		 * Compute vectors v0 = C - A v1 = B - A v2 = P - A
		 */

		Vector3d v1 = new Vector3d(third);
		Vector3d v0 = new Vector3d(second);
		Vector3d v2 = new Vector3d(testPoint);
		v2.sub(first);
		v1.sub(first);
		v0.sub(first);

		double dot00 = v0.dot(v0);
		double dot01 = v0.dot(v1);
		double dot02 = v0.dot(v2);
		double dot11 = v1.dot(v1);
		double dot12 = v1.dot(v2);

		// Compute barycentric coordinates
		double invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
		double u = (dot11 * dot02 - dot01 * dot12) * invDenom;
		double v = (dot00 * dot12 - dot01 * dot02) * invDenom;

		// Check if point is in triangle
		if (u >= 0 && v >= 0 && u + v < 1) {
			return true;
		}
		return false;

	}

	public Point3d getFirst() {
		return (Point3d) first.clone();
	}


	public Point3d getSecond() {
		return (Point3d) second.clone();
	}


	public Point3d getThird() {
		return (Point3d) third.clone();
	}

	

}
